package emysql

import (
	"context"
	"database/sql"

	"xorm.io/xorm"
)

/*
 * 复写xorm.e中常用函数
 * 缺少的方法可以按照以下方式复写
 */

// Where method provide a condition query
func (e *EMysql) Where(ctx context.Context, query interface{}, args ...interface{}) *xorm.Session {
	return e.ESession(ctx).Where(query, args...)
}

// ID method provoide a condition as (id) = ?
func (e *EMysql) ID(ctx context.Context, id interface{}) *xorm.Session {
	return e.ESession(ctx).ID(id)
}

// Distinct use for distinct columns. Caution: when you are using cache,
// distinct will not be cached because cache system need id,
// but distinct will not provide id
func (e *EMysql) Distinct(ctx context.Context, columns ...string) *xorm.Session {
	return e.ESession(ctx).Distinct(columns...)
}

// Select customerize your select columns or contents
func (e *EMysql) Select(ctx context.Context, str string) *xorm.Session {
	return e.ESession(ctx).Select(str)
}

// Cols only use the parameters as select or update columns
func (e *EMysql) Cols(ctx context.Context, columns ...string) *xorm.Session {
	return e.ESession(ctx).Cols(columns...)
}

// AllCols indicates that all columns should be use
func (e *EMysql) AllCols(ctx context.Context) *xorm.Session {
	return e.ESession(ctx).AllCols()
}

// MustCols specify some columns must use even if they are empty
func (e *EMysql) MustCols(ctx context.Context, columns ...string) *xorm.Session {
	return e.ESession(ctx).MustCols(columns...)
}

// In will generate "column IN (?, ?)"
func (e *EMysql) In(ctx context.Context, column string, args ...interface{}) *xorm.Session {
	return e.ESession(ctx).In(column, args...)
}

// NotIn will generate "column NOT IN (?, ?)"
func (e *EMysql) NotIn(ctx context.Context, column string, args ...interface{}) *xorm.Session {
	return e.ESession(ctx).NotIn(column, args...)
}

// Incr provides a update string like "column = column + ?"
func (e *EMysql) Incr(ctx context.Context, column string, arg ...interface{}) *xorm.Session {
	return e.ESession(ctx).Incr(column, arg...)
}

// Decr provides a update string like "column = column - ?"
func (e *EMysql) Decr(ctx context.Context, column string, arg ...interface{}) *xorm.Session {
	return e.ESession(ctx).Decr(column, arg...)
}

// Alias set the table alias
func (e *EMysql) Alias(ctx context.Context, alias string) *xorm.Session {
	return e.ESession(ctx).Alias(alias)
}

// Limit will generate "LIMIT start, limit"
func (e *EMysql) Limit(ctx context.Context, limit int, start ...int) *xorm.Session {
	return e.ESession(ctx).Limit(limit, start...)
}

// Desc will generate "ORDER BY column1 DESC, column2 DESC"
func (e *EMysql) Desc(ctx context.Context, colNames ...string) *xorm.Session {
	return e.ESession(ctx).Desc(colNames...)
}

// Asc will generate "ORDER BY column1,column2 Asc"
// This method can chainable use.
//
//        e.Desc("name").Asc("age").Find(&users)
//        // SELECT * FROM user ORDER BY name DESC, age ASC
//
func (e *EMysql) Asc(ctx context.Context, colNames ...string) *xorm.Session {
	return e.ESession(ctx).Asc(colNames...)
}

// OrderBy will generate "ORDER BY order"
func (e *EMysql) OrderBy(ctx context.Context, order string) *xorm.Session {
	return e.ESession(ctx).OrderBy(order)
}

// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
func (e *EMysql) Join(ctx context.Context, joinOperator string, tablename interface{}, condition string, args ...interface{}) *xorm.Session {
	return e.ESession(ctx).Join(joinOperator, tablename, condition, args...)
}

// GroupBy generate group by statement
func (e *EMysql) GroupBy(ctx context.Context, keys string) *xorm.Session {
	return e.ESession(ctx).GroupBy(keys)
}

// Having generate having statement
func (e *EMysql) Having(ctx context.Context, conditions string) *xorm.Session {
	return e.ESession(ctx).Having(conditions)
}

// Exec raw sql
func (e *EMysql) Exec(ctx context.Context, sqlOrArgs ...interface{}) (sql.Result, error) {
	return e.ESession(ctx).Exec(sqlOrArgs...)
}

// Query a raw sql and return records as []map[string][]byte
func (e *EMysql) Query(ctx context.Context, sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Query(sqlOrArgs...)
}

// QueryString runs a raw sql and return records as []map[string]string
func (e *EMysql) QueryString(ctx context.Context, sqlOrArgs ...interface{}) ([]map[string]string, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.QueryString(sqlOrArgs...)
}

// QueryInterface runs a raw sql and return records as []map[string]interface{}
func (e *EMysql) QueryInterface(ctx context.Context, sqlOrArgs ...interface{}) ([]map[string]interface{}, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.QueryInterface(sqlOrArgs...)
}

// Insert one or more records
func (e *EMysql) Insert(ctx context.Context, beans ...interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Insert(beans...)
}

// InsertOne insert only one record
func (e *EMysql) InsertOne(ctx context.Context, bean interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.InsertOne(bean)
}

// Update records, bean's non-empty fields are updated contents,
// condiBean' non-empty filds are conditions
// CAUTION:
//        1.bool will defaultly be updated content nor conditions
//         You should call UseBool if you have bool to use.
//        2.float32 & float64 may be not inexact as conditions
func (e *EMysql) Update(ctx context.Context, bean interface{}, condiBeans ...interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Update(bean, condiBeans...)
}

// Delete records, bean's non-empty fields are conditions
func (e *EMysql) Delete(ctx context.Context, bean interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Delete(bean)
}

// Get retrieve one record from table, bean's non-empty fields
// are conditions
func (e *EMysql) Get(ctx context.Context, bean interface{}) (bool, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Get(bean)
}

// Exist returns true if the record exist otherwise return false
func (e *EMysql) Exist(ctx context.Context, bean ...interface{}) (bool, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Exist(bean...)
}

// Find retrieve records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
func (e *EMysql) Find(ctx context.Context, beans interface{}, condiBeans ...interface{}) error {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Find(beans, condiBeans...)
}

// FindAndCount find the results and also return the counts
func (e *EMysql) FindAndCount(ctx context.Context, rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.FindAndCount(rowsSlicePtr, condiBean...)
}

// Iterate record by record handle records from table, bean's non-empty fields
// are conditions.
func (e *EMysql) Iterate(ctx context.Context, bean interface{}, fun xorm.IterFunc) error {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Iterate(bean, fun)
}

// Count counts the records. bean's non-empty fields are conditions.
func (e *EMysql) Count(ctx context.Context, bean ...interface{}) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Count(bean...)
}

// Sum sum the records by some column. bean's non-empty fields are conditions.
func (e *EMysql) Sum(ctx context.Context, bean interface{}, colName string) (float64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Sum(bean, colName)
}

// SumInt sum the records by some column. bean's non-empty fields are conditions.
func (e *EMysql) SumInt(ctx context.Context, bean interface{}, colName string) (int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.SumInt(bean, colName)
}

// Sums sum the records by some columns. bean's non-empty fields are conditions.
func (e *EMysql) Sums(ctx context.Context, bean interface{}, colNames ...string) ([]float64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.Sums(bean, colNames...)
}

// SumsInt like Sums but return slice of int64 instead of float64.
func (e *EMysql) SumsInt(ctx context.Context, bean interface{}, colNames ...string) ([]int64, error) {
	session := e.ESession(ctx)
	defer session.Close()
	return session.SumsInt(bean, colNames...)
}
